package com.scorpio.rotatemenu;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RutateMenuView extends View {
    private final boolean DEBUG = false;

    private final String TAG = this.getClass().getSimpleName();
    private final int VERSE_ONE_ANIMATION = 1;
    //默认宽度DP
    private final int DEFAULT_WIDTH = 250;
    //默认高度DP
    private final int DEFAULT_HEIGHT = 500;
    //默认字体SP
    private final int DEFAULT_TEXT_SIZE = 14;
    //默认圆圈的线宽DP
    private final int DEFAULT_CIRCLE_WIDTH = 1;
    //默认Icon的描边宽DP
    private final int DEFAULT_CONTOUR_WIDTH = 2;
    //默认Icon的大小
    private final int DEFAULT_ITEM_WIDTH = 25;
    //默认内圆的半径
    private final int DEFAULT_OUTER_RING_RADIUS = 180;
    //默认缩放最大倍数
    private final float DEFAULT_MAXZOOM = 0.5f;

    //每帧速度
    private final long frameSpeed = 1000 / 60;
    //动画持续帧
    private int animationDurationFrame = 15;

    //当前惯性值
    private volatile int velocityY;
    //view宽度
    private int width;
    //viee高度
    private int height;
    //每个item角度间隔
    int itemAngleInterval = 45;
    //当前旋转角度
    int slideAngle = 0;
    //字体大小
    int textSize = 0;
    //字体颜色
    int textColot = Color.WHITE;
    //选中的颜色
    int selectedTextColor = Color.YELLOW;
    //圆圈的颜色
    int circleColor = Color.WHITE;
    //圆圈的线宽
    int circleWidth = 0;
    //描边颜色
    int iconColor = Color.WHITE;
    //Icon描边宽度
    int iconWidth = 0;
    //当前菜单索引
    int index = -1;
    //缩放最大倍数
    private float maxZoom = 0.5f;
    //整体偏移角度
    private int offsetAngle = 0;
    //突出角度
    private int extrudeAngle = 0;
    private GestureDetector gestureDetector;

    private Map<ScorpioEntity, RutateMenuItem> menuItems = new HashMap<>();

    //当前时候正在进行中动画
    private boolean isExercise = false;
    private Paint mCirclePaint;
    private Paint mIconPaint;
    private Paint mFontPaint;
    private List<ScorpioEntity> mData = new ArrayList<>();

    private int itemWidth;
    private int outerRingRadius;
    private int innerRingRadius;

    public RutateMenuView(Context context) {
        this(context, null);
    }

    public RutateMenuView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RutateMenuView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RutateMenuView);
        if (typedArray != null) {
            offsetAngle = typedArray.getInteger(R.styleable.RutateMenuView_offsetAngle, offsetAngle);
            extrudeAngle = typedArray.getInteger(R.styleable.RutateMenuView_extrudeAngle, extrudeAngle);
            extrudeAngle = extrudeAngle > 45 ? 45 : extrudeAngle < 0 ? 0:extrudeAngle;
            maxZoom = typedArray.getFraction(R.styleable.RutateMenuView_maxZoom, 1, 1, DEFAULT_MAXZOOM);
            itemWidth = (int) typedArray.getDimension(R.styleable.RutateMenuView_itemWidth, DensityUtil.dip2px(getContext(), DEFAULT_ITEM_WIDTH));
            outerRingRadius = (int) typedArray.getDimension(R.styleable.RutateMenuView_outerRingRadius, DensityUtil.dip2px(getContext(), DEFAULT_OUTER_RING_RADIUS));
            innerRingRadius = outerRingRadius + itemWidth + itemWidth / 4;
            textSize = (int) typedArray.getDimension(R.styleable.RutateMenuView_meunTextSize, DensityUtil.sp2px(context, DEFAULT_TEXT_SIZE));
            textColot = typedArray.getColor(R.styleable.RutateMenuView_meunTextColor, textColot);
            selectedTextColor = typedArray.getColor(R.styleable.RutateMenuView_meunSelectedTextColor, selectedTextColor);
            circleColor = typedArray.getColor(R.styleable.RutateMenuView_circleColor, circleColor);
            iconColor = typedArray.getColor(R.styleable.RutateMenuView_iconColor, iconColor);
            circleWidth = (int) typedArray.getDimension(R.styleable.RutateMenuView_circleWidth, DensityUtil.sp2px(context, DEFAULT_CIRCLE_WIDTH));
            iconWidth = (int) typedArray.getDimension(R.styleable.RutateMenuView_iconWidth, DensityUtil.sp2px(context, DEFAULT_CONTOUR_WIDTH));
            itemAngleInterval = (int) (typedArray.getFloat(R.styleable.RutateMenuView_itemAngleInterval, itemAngleInterval) * 100);
        }
        gestureDetector = new GestureDetector(context, listener);
        init();
    }

    private void init() {
        //创建画笔
        mCirclePaint = new Paint();
        //设置画笔颜色
        mCirclePaint.setColor(circleColor);
        mCirclePaint.setStrokeWidth(circleWidth);
        //设置画笔的样式
        mCirclePaint.setStyle(Paint.Style.STROKE);
        mCirclePaint.setAntiAlias(true);

        //创建画笔
        mIconPaint = new Paint();
        //设置画笔颜色
        mIconPaint.setColor(iconColor);
        mIconPaint.setStrokeWidth(iconWidth);
        //设置画笔的样式
        mIconPaint.setStyle(Paint.Style.STROKE);
        mIconPaint.setAntiAlias(true);

        mFontPaint = new Paint();
        mFontPaint.setColor(textColot);
        mFontPaint.setTextSize(textSize);
        mFontPaint.setStyle(Paint.Style.FILL);
        mFontPaint.setAntiAlias(true);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        int x = 0-(int) (innerRingRadius * Math.cos((90 + extrudeAngle) * Math.PI / 180));
        int y = height / 2;
        mCirclePaint.setStrokeWidth(circleWidth);
        mIconPaint.setColor(circleColor);
        canvas.drawCircle(x, y, outerRingRadius, mCirclePaint);
        canvas.drawCircle(x, y, innerRingRadius, mCirclePaint);
        synchronized (this) {
            if (mData.size() > 0) {
                int length = mData.size();
                int ta = this.slideAngle % this.itemAngleInterval;
                int firsPlaceIndex = (this.slideAngle / this.itemAngleInterval + (Math.abs(ta) > this.itemAngleInterval / 2 ? (ta > 0 ? 1 : -1) : 0)) % length;
                int drawIndex = 0 - firsPlaceIndex;
                if (drawIndex < 0) {
                    drawIndex = length + drawIndex;
                }
                Log.d(TAG, "" + drawIndex);
                float slideAngle = this.slideAngle / 100f;
                //绘制下半部分
                for (int i = 0; i < length; i++) {
                    ScorpioEntity entity = mData.get(i);
                    RutateMenuItem menuItem = menuItems.get(entity);
                    menuItem.setCentre(x, y);
                    menuItem.setOffsetAngle(offsetAngle);
                    if (menuItem.setAngle(getSlideAngle(i), slideAngle, 0, 0, 90 + extrudeAngle - offsetAngle)) {
                        canvas.drawCircle(menuItem.getX(), menuItem.getY(), menuItem.getSize(), mIconPaint);
                        canvas.drawBitmap(menuItem.bitmap, menuItem.getSrc(), menuItem.getDst(), mIconPaint);
                        mFontPaint.setColor(i == drawIndex ? selectedTextColor : textColot);
                        mFontPaint.setTextSize(textSize * menuItem.getZoom());
                        canvas.drawText(entity.getTitle(), menuItem.getTextX(textSize), menuItem.getTextY(textSize), mFontPaint);
                        if (DEBUG) {
                            canvas.drawText("Angle:" + menuItem.getAngle(), menuItem.getTextX(textSize), menuItem.getTextY((int) (-textSize * 2 * menuItem.getZoom())), mFontPaint);
                            canvas.drawRect(menuItem.getLeft(), menuItem.getTop(), menuItem.getRight(), menuItem.getBottom(), mCirclePaint);
                        }
                    }
                }
                //绘制上半部分
                for (int i = 0; i < length; i++) {
                    ScorpioEntity entity = mData.get(i);
                    RutateMenuItem menuItem = menuItems.get(entity);
                    if (menuItem.isShow()) continue;
                    menuItem.setCentre(x, y);
                    menuItem.setOffsetAngle(offsetAngle);
                    float maxAngle = (length * itemAngleInterval) / 100f;
                    float slideOffsetAngle = 360 - maxAngle;
                    if (menuItem.setAngle(getSlideAngle(i), slideAngle, slideOffsetAngle, 270 - extrudeAngle - offsetAngle, 359.99f)) {
                        canvas.drawCircle(menuItem.getX(), menuItem.getY(), menuItem.getSize(), mIconPaint);
                        canvas.drawBitmap(menuItem.bitmap, menuItem.getSrc(), menuItem.getDst(), mIconPaint);
                        mFontPaint.setColor(i == drawIndex ? selectedTextColor : textColot);
                        mFontPaint.setTextSize(textSize * menuItem.getZoom());
                        canvas.drawText(entity.getTitle(), menuItem.getTextX(textSize), menuItem.getTextY(textSize), mFontPaint);
                        if (DEBUG) {
                            canvas.drawText("Angle:" + menuItem.getAngle(), menuItem.getTextX(textSize), menuItem.getTextY((int) (-textSize * 2 * menuItem.getZoom())), mFontPaint);
                            canvas.drawRect(menuItem.getLeft(), menuItem.getTop(), menuItem.getRight(), menuItem.getBottom(), mCirclePaint);
                        }
                    }
                }

            }
            if (DEBUG) {
                RutateMenuItem menuItem = new RutateMenuItem(innerRingRadius, itemWidth, maxZoom, 0);
                menuItem.setOffsetAngle(offsetAngle);
                menuItem.setAngle(0, 0);
                canvas.drawLine(x, y, menuItem.getX(), y + menuItem.getY(), mCirclePaint);
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        stopExercise();
        handler.removeCallbacksAndMessages(null);
        super.onDetachedFromWindow();
    }

    //根据索引获取旋转角度
    private float getSlideAngle(int i) {
        return (i * itemAngleInterval) / 100f;
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            synchronized (RutateMenuView.class) {
                if (!isExercise) return;
                if (velocityY != 0) {
                    int distanceY = velocityY / animationDurationFrame;
                    //惯性动画
                    int t = itemAngleInterval / (animationDurationFrame * 10);
                    if (Math.abs(distanceY) < t || Math.abs(t) <= 1) {
                        slideAngle += velocityY;
                        velocityY = 0;
                    } else {
                        slideAngle += distanceY;
                        velocityY -= distanceY;
                    }
                } else {
                    int correctionAngle = getCorrectionAngle(slideAngle);
                    if (correctionAngle == 0) {
                        isExercise = false;
                        updateIndex();
                    } else {
                        velocityY = correctionAngle;
                    }
                }
                postInvalidate();
                handler.sendEmptyMessageDelayed(msg.what, frameSpeed);
            }
        }
    };

    /**
     * 获取 还需要更修正的角度
     *
     * @param angle
     * @return 0无需更正
     */
    private int getCorrectionAngle(int angle) {
        int correctionSlideAngle = correction(angle);
        return correctionSlideAngle - angle;
    }

    /**
     * 修正距离位置
     *
     * @return true/修正中
     */
    private int correction(int distance) {
        //向下移动的
        boolean downSlide = distance > 0;
        int differ = Math.abs(distance) % itemAngleInterval;
        int correct = 0;
        if (differ != 0) {
            if (differ > itemAngleInterval / 2) {
                correct = differ - itemAngleInterval;
            } else {
                correct = differ;
            }
        }
        return distance - (downSlide ? correct : -correct);
    }

    private float slideY = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mData.size() != 0) {
            gestureDetector.onTouchEvent(event);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    slideY = event.getY();
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    if (velocityY == 0) {
                        velocityY += getCorrectionAngle(slideAngle);
                    }
                    if (velocityY != 0) {
                        startExercise();
                        postInvalidate();
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    float cy = event.getY() - slideY;
                    float ts = (DensityUtil.px2dip(getContext(), cy) / 2f) * 100;
                    slideAngle += Math.abs(ts) < 1 ? cy < 0 ? -1 : 1 : ts;
                    slideY = event.getY();
                    invalidate();
                    break;
            }
        }
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        width = widthMode == MeasureSpec.EXACTLY ? widthSize : DensityUtil.dip2px(getContext(), DEFAULT_WIDTH);
        height = heightMode == MeasureSpec.EXACTLY ? heightSize : DensityUtil.dip2px(getContext(), DEFAULT_HEIGHT);
        setMeasuredDimension(width, height);
    }

    private GestureDetector.OnGestureListener listener = new GestureDetector.OnGestureListener() {

        @Override
        public boolean onDown(MotionEvent e) {
            velocityY = 0;
            stopExercise();
            return false;
        }

        @Override
        public void onShowPress(MotionEvent e) {
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            if (!isExercise) {
                int index = getClickItem(e.getX(), e.getY());
                if (index != -1) {
                    if (onMenuItemClickListener != null) {
                        onMenuItemClickListener.onMenuItemClick(index);
                    }
                }
            }
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//            slideAngle += (DensityUtil.px2dip(getContext(), -distanceY) / 2) * 100;
//            invalidate();
            return false;
        }

        // 用户长按触摸屏，由多个MotionEvent ACTION_DOWN触发
        @Override
        public void onLongPress(MotionEvent e) {
        }

        // 用户按下触摸屏、快速移动后松开，由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            RutateMenuView.this.velocityY = (DensityUtil.px2dip(getContext(), velocityY) / 20) * 100;
            return false;
        }
    };

    /**
     * 根据坐标获取实际点击的菜单
     *
     * @param x
     * @param y
     * @return
     */
    private int getClickItem(float x, float y) {
        synchronized (menuItems) {
            for (ScorpioEntity entity : menuItems.keySet()) {
                RutateMenuItem menuItem = menuItems.get(entity);
                if (menuItem.isShow())
                    if (x > menuItem.getLeft() && x < menuItem.getRight() && y > menuItem.getTop() && y < menuItem.getBottom()) {
                        return mData.indexOf(entity);
                    }
            }
        }
        return -1;
    }

    /**
     * 设置选中指定索引菜单
     *
     * @param index     指定索引
     * @param animation 播放动画
     */
    public void setSelection(int index, boolean animation) {
        if (index < 0 || index > mData.size()) {
            Log.d(TAG, "data size=" + mData.size() + ",index=" + index);
            return;
        }
        synchronized (RutateMenuView.class) {
            if (animation) {
                if (isExercise) {
                    initMeun();
                }
                slideAngle = correction(slideAngle);
                velocityY = -((index - this.index) * itemAngleInterval);
                startExercise();
            } else {
                initMeun();
                velocityY = 0;
                slideAngle = -(index * itemAngleInterval);
                this.index = index;
                if (onMenuScrollChangedListener != null) {
                    onMenuScrollChangedListener.onMenuChanged(index);
                }
            }
            postInvalidate();
        }
    }

    /**
     * 设置数据
     *
     * @param data
     */
    public void setData(List<ScorpioEntity> data) {
        synchronized (RutateMenuView.class) {
            this.mData = data;
            initMeun();
            updateDate();
            postInvalidate();
        }
    }

    //初始化菜单参数
    private void initMeun() {
        slideAngle = 0;
        index = 0;
        velocityY = 0;
        stopExercise();
    }

    private void stopExercise() {
        isExercise = false;
        handler.removeMessages(VERSE_ONE_ANIMATION);
    }

    //开始执行动画刷新
    private void startExercise() {
        isExercise = true;
        handler.sendEmptyMessage(VERSE_ONE_ANIMATION);
    }

    //刷新当前索引
    private synchronized void updateIndex() {
        for (ScorpioEntity entity : menuItems.keySet()) {
            RutateMenuItem menuItem = menuItems.get(entity);
            if (menuItem.isShow() && menuItem.getAngle() == getSlideAngle(0)) {
                index = mData.indexOf(entity);
                if (index != -1) {
                    if (onMenuScrollChangedListener != null) {
                        onMenuScrollChangedListener.onMenuChanged(index);
                    }
                }
                return;
            }
        }
    }

    //刷新菜单数据
    private synchronized void updateDate() {
        Map<ScorpioEntity, RutateMenuItem> menuItems = new HashMap<>();
        for (ScorpioEntity entity : mData) {
            RutateMenuItem menuItem = new RutateMenuItem(
                    innerRingRadius
                    , itemWidth
                    , maxZoom
                    , (itemAngleInterval * mData.size()) / 100f
            );
            menuItem.bitmap = ((BitmapDrawable) getResources().getDrawable(entity.getResId())).getBitmap();
            menuItem.setMaxZoom(maxZoom);
            menuItem.setWidth(itemWidth);
            menuItems.put(entity, menuItem);
        }
        this.menuItems = menuItems;
    }

    private OnMenuItemClickListener onMenuItemClickListener;
    private OnMenuScrollChangedListener onMenuScrollChangedListener;

    public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
        this.onMenuItemClickListener = onMenuItemClickListener;
    }

    public void setOnMenuScrollChangedListener(OnMenuScrollChangedListener onMenuScrollChangedListener) {
        this.onMenuScrollChangedListener = onMenuScrollChangedListener;
    }

    public int getIndex() {
        return index;
    }

    public interface OnMenuItemClickListener {
        //点击的菜单索引
        void onMenuItemClick(int index);
    }

    public interface OnMenuScrollChangedListener {
        //滑动的菜单索引
        void onMenuChanged(int index);
    }

    /**
     * 设置默认字体大小
     *
     * @param textSize
     */
    public void setTextSize(int textSize) {
        this.textSize = textSize;
        postInvalidate();
    }

    /**
     * 设置默认字体颜色
     *
     * @param textColot
     */
    public void setTextColot(int textColot) {
        this.textColot = textColot;
        postInvalidate();
    }

    /**
     * 设置选中字体颜色
     *
     * @param selectedTextColor
     */
    public void setSelectedTextColor(int selectedTextColor) {
        this.selectedTextColor = selectedTextColor;
        postInvalidate();
    }

    /**
     * 圆盘颜色
     *
     * @param circleColor
     */
    public void setCircleColor(int circleColor) {
        this.circleColor = circleColor;
        postInvalidate();
    }

    /**
     * 圆盘线条宽度
     *
     * @param circleWidth
     */
    public void setCircleWidth(int circleWidth) {
        this.circleWidth = circleWidth;
        postInvalidate();
    }

    /**
     * icon描边颜色
     *
     * @param iconColor
     */
    public void setIconColor(int iconColor) {
        this.iconColor = iconColor;
        postInvalidate();
    }

    /**
     * icon描边宽度
     *
     * @param iconWidth
     */
    public void setIconWidth(int iconWidth) {
        this.iconWidth = iconWidth;
        postInvalidate();
    }

    /**
     * 最大缩小比例
     *
     * @param maxZoom
     */
    public void setMaxZoom(float maxZoom) {
        this.maxZoom = maxZoom;
        updateDate();
        postInvalidate();
    }

    /**
     * 整体偏移角度 最大360度
     *
     * @param offsetAngle
     */
    public void setOffsetAngle(int offsetAngle) {
        this.offsetAngle = offsetAngle;
        updateDate();
        postInvalidate();
    }

    /**
     * 设置1：1的大小
     *
     * @param size
     */
    public void setIconSize(int size) {
        this.itemWidth = size;
        updateDate();
        postInvalidate();
    }

    /**
     * 设置每个item的间隔角度
     *
     * @param itemAngleInterval
     */
    public void setItemAngleInterval(float itemAngleInterval) {
        this.itemAngleInterval = (int) (itemAngleInterval * 100);
        updateDate();
        postInvalidate();
    }

    /**
     * 偏移突出角度
     * @param extrudeAngle
     */
    public void setExtrudeAngle(int extrudeAngle) {
        this.extrudeAngle = extrudeAngle;
        updateDate();
        postInvalidate();
    }
}
